/*
 * Copyright (c) Huawei Device Co., Ltd. 2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <securec.h>
#include "der_parser.h"

#include "loghelper.h"

namespace OHOS {
namespace SE {
static const uint8_t MAX_TLV_SIZE = 128;
static const uint8_t SEVEN_BIT_SHIFT = 7;
static const uint8_t EIGHT_BIT_SHIFT = 8;
static const uint8_t SUB_ID_SIGN = 79;
static const uint8_t SUB_ID_DIVIDER = 40;

DerParser::DerParser(const std::vector<uint8_t> &derData)
{
    derData_ = derData;
    derCurrentIndex_ = 0;
    derDataLength_ = derData.size();
    tlvDataLength_ = derDataLength_;
    if (derDataLength_ == 0) {
        ErrorLog("der data is empty");
        return;
    }
    if (derData_[derCurrentIndex_] == ASN1_PADDING_BYTE) {
        tlvDataLength_ = 0;
        while (++derCurrentIndex_ < derDataLength_) {
            if (derData_[derCurrentIndex_] != ASN1_PADDING_BYTE) {
                ErrorLog("Invalid file data");
                return;
            }
        }
    }
}

DerParser::~DerParser()
{
}

uint8_t DerParser::GetTlvType()
{
    uint8_t type = 0;
    GetTlvTypeInner(type);
    GetTlvSize(tlvDataLength_);
    return type;
}

bool DerParser::GetTlvTypeInner(uint8_t &tlvType)
{
    if (IsParsingEnd()) {
        ErrorLog("already end of data.");
        return false;
    }
    tlvType = derData_[derCurrentIndex_++];
    return true;
}

bool DerParser::GetTlvSize(uint16_t &tlvSize)
{
    if (IsParsingEnd()) {
        ErrorLog("already end of data.");
        return false;
    }

    tlvSize = derData_[derCurrentIndex_++];
    if (tlvSize >= MAX_TLV_SIZE) {
        uint16_t size = tlvSize - MAX_TLV_SIZE;
        for (tlvSize = 0; size > 0; size--) {
            if (IsParsingEnd()) {
                ErrorLog("already end of data, skip parsing.");
                return false;
            } else {
                tlvSize = (tlvSize << EIGHT_BIT_SHIFT) + derData_[derCurrentIndex_++];
            }
        }
    }

    if ((derCurrentIndex_ + tlvSize) > derDataLength_) {
        ErrorLog("data exceed, invalid data size.");
        return false;
    }
    return true;
}

uint16_t DerParser::GetTlvDataLength(uint8_t type)
{
    uint8_t tlvType = 0;
    GetTlvTypeInner(tlvType);
    if (tlvType == type) {
        GetTlvSize(tlvDataLength_);
    } else {
        ErrorLog("type[%{public}u], tlv type in der[%{public}u]", type, tlvType);
        return 0;
    }
    return tlvDataLength_;
}

std::vector<uint8_t> DerParser::GetTlvData()
{
    std::vector<uint8_t> data(tlvDataLength_);
    auto ret = memcpy_s(&data[0], tlvDataLength_, &derData_[derCurrentIndex_], tlvDataLength_);
    if (ret != 0) {
        ErrorLog("memcpy_s failed");
        return std::vector<uint8_t>();
    }
    derCurrentIndex_ += tlvDataLength_;
    return data;
}

bool DerParser::IsParsingEnd()
{
    if (derCurrentIndex_ == derDataLength_) {
        return true;
    }
    if (derData_[derCurrentIndex_] == ASN1_PADDING_BYTE) {
        while (++derCurrentIndex_ < derDataLength_) {
            if (derData_[derCurrentIndex_] != ASN1_PADDING_BYTE) {
                ErrorLog("invalid file data.");
                return true;
            }
        }
        return true;
    }
    return false;
}

void DerParser::SkipTlvData()
{
    derCurrentIndex_ += tlvDataLength_;
}

std::vector<uint8_t> DerParser::ParseEfPath()
{
    GetTlvDataLength(ASN1_SEQUENCE);
    GetTlvDataLength(ASN1_OCTET_STRING);
    return GetTlvData();
}

std::string DerParser::ParseDodfOid()
{
    std::string oid = "";
    if (GetTlvDataLength(ASN1_OID) == 0) {
        ErrorLog("get invalid oid.");
        return oid;
    }
    uint16_t maxIndex = derCurrentIndex_ + tlvDataLength_;
    uint32_t subid = ParseSubIdOfOid();
    if (subid <= SUB_ID_SIGN) {
        // reference: GlobalPlatform Device Technology Secyre Element Access Control Spec.
        oid.append(std::to_string(subid / SUB_ID_DIVIDER));
        oid.append(".");
        oid.append(std::to_string(subid % SUB_ID_DIVIDER));
    } else {
        oid.append("2.");
        oid.append(std::to_string(subid - SUB_ID_SIGN - 1));
    }

    while (derCurrentIndex_ < maxIndex) {
        oid.append(".");
        oid.append(std::to_string(ParseSubIdOfOid()));
    }
    InfoLog("get oid [%{public}s]", oid.c_str());
    return oid;
}

uint32_t DerParser::ParseSubIdOfOid()
{
    uint32_t subId = 0;
    do {
        subId = (subId << SEVEN_BIT_SHIFT) + (derData_[derCurrentIndex_] & 0x7F); // 0x7F refers to lower 7 bit mask
    } while ((derData_[derCurrentIndex_++] >> SEVEN_BIT_SHIFT) != 0);
    return subId;
}

void DerParser::GetParsingStatus(uint8_t &currentIndex, uint8_t &tlvDataLength)
{
    currentIndex = derCurrentIndex_;
    tlvDataLength = tlvDataLength_;
}

void DerParser::RecoverParsingStatus(uint8_t currentIndex, uint8_t tlvDataLength)
{
    if (tlvDataLength > derDataLength_) {
        ErrorLog("invalid data length.");
        return;
    }
    derCurrentIndex_ = currentIndex;
    tlvDataLength_ = tlvDataLength;
}
}  // namespace SE
}  // namespace OHOS
